﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using RainEngine.Controllers;
using RainEngine.Rendering;


namespace RainEngine.SceneManagement
{
    /// <summary>
    /// SceneManager负责管理一个scene集合，创建，添加，初始化scene对象。
    /// 它还负责更新和绘制当前scene
    /// </summary>
    public sealed class SceneManager : DrawableGameComponent
    {
        #region 构造函数和成员变量

        // 对引擎的引用
        private RainGE engine;

        // 拾取处理程序
        private Pickor pickor;


        /// <summary>
        /// 获取拾取处理程序
        /// </summary>
        public Pickor Pickor
        {
            get { return pickor; }
        }        

        /// <summary>
        /// 所有scene的集合
        /// </summary>
        List<Scene> scenes = new List<Scene>();

        /// <summary>
        /// scene集合的副本
        /// </summary>
        List<Scene> scenesToUpdate = new List<Scene>();

        /// <summary>
        /// 当前激活的屏幕
        /// </summary>
        Scene activeScene;

        /// <summary>
        /// 存储scene名称和ID的Dictionary
        /// </summary>
        private Dictionary<string, int> sceneIdForNames = new Dictionary<string, int>();

        /// <summary>
        /// 初始的sceneId，默认为0
        /// </summary>
        private int sceneId = 0;

        /// <summary>
        /// 是否已经初始化
        /// </summary>
        bool isInitialized;

        /// <summary>
        /// 用于统计帧数信息的结构体
        /// </summary>
        private RenderStatisticsAggregate lastStatAggregate, currentStatAggregate;        

        /// <summary>
        /// 统计帧频的持续时间，默认为250毫秒，即每隔250毫秒刷新一次帧频。
        /// 不要改变这个常数，否则fps是无意义的。
        /// </summary>
        public const int AGGREGATION_MILLISECONDS = 250;

        /// <summary>
        /// 帧频
        /// </summary>      
        private float lastFps = 0;        

        /// <summary>
        /// 私有构造函数，不会被使用
        /// </summary>
        private SceneManager() : base(null) { }

        /// <summary>
        /// 创建一个新SceneManager对象。
        /// </summary>
        /// <param name="engine"></param>
        internal SceneManager(RainGE engine)
            : base(engine)
        {
            this.engine = engine;
            pickor = new Pickor(engine,this);
        }

        #endregion

        #region 属性

        /// <summary>
        /// 获取当前激活的scene
        /// </summary>
        public Scene ActiveScene
        {
            get { return activeScene; }
            set { activeScene = value; }
        }

        /// <summary>
        /// Scene索引器
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public Scene this[int index] { get { return scenes[index]; } }

        /// <summary>
        /// 返回Scene对象数量
        /// </summary>
        public int Scenes { get { return scenes.Count; } }

        /// <summary>
        /// 获取是否显示光标
        /// </summary>
        /// <returns>Bool</returns>
        public bool isShowMouseCursor
        {
            get
            {
                return ((scenes.Count > 0) && (activeScene.IsShowMouse == true));
            }
        }

        /// <summary>
        /// 获取FPS。FPS会在每隔AGGREGATION_MILLISECONDS（定义为250）毫秒时间内重新计算。 
        /// </summary>
        public float Fps
        {
            get { return lastFps; }
        }

        #endregion

        #region DrawableGameComponent重载方法

        /// <summary>
        /// 初始化SCM
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();            

            // 让所有屏幕加载内容
            foreach (Scene scene in scenes)
            {
                scene.Initialize();
            }

            isInitialized = true;            

            // 重置计时器用于计算帧频率等 
            currentStatAggregate.Reset();
        }

        /// <summary>
        /// 移除内容
        /// </summary>
        protected override void UnloadContent()
        {
            // // 让所有屏幕移除内容
            foreach (Scene scene in scenes)
            {
                scene.UnloadContent();
            }
        }

        /// <summary>
        /// 更新激活scene的节点
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Update(GameTime gameTime)
        {
            //  获取当前流逝的时间，单位为毫秒
            long milis = currentStatAggregate.aggregateTimer.ElapsedMilliseconds;

            // 创建一个屏幕集合的副本以防止添加或移除屏幕时可能产生的冲突
            scenesToUpdate.Clear();

            foreach (Scene scene in scenes)
                scenesToUpdate.Add(scene);

            bool otherScreenHasFocus = !Game.IsActive;
            bool coveredByOtherScreen = false;

            // 更新所有的scene
            while (scenesToUpdate.Count > 0)
            {
                // 将集合顶部的scene移除
                Scene scene = scenesToUpdate[scenesToUpdate.Count - 1];
                scenesToUpdate.RemoveAt(scenesToUpdate.Count - 1);

                // 更新当前scene
                scene.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

                if (scene.ScreenState == ScreenState.TransitionOn || scene.ScreenState == ScreenState.Active)
                {
                    //如果此屏幕是第一个激活屏幕，则处理它的自定义输入
                    if (!otherScreenHasFocus)
                    {
                        scene.HandleInput();
                        otherScreenHasFocus = true;
                    }
                    // 如果这不是一个弹出scene，告知其下的屏幕它们被这个屏幕覆盖
                    if (!scene.IsPopup)
                        coveredByOtherScreen = true;
                }
            }

            //只更新激活scene的UI控件
            if (activeScene != null)
            {
                activeScene.UiManager.Update(gameTime);
                //更新相机控制器
                if (activeScene.Camera != null)
                    activeScene.Camera.Update(gameTime);

                //将update调用次数加1
                currentStatAggregate.updateCount++;

                //------------------------------------
                //  Update用了多少毫秒?
                //------------------------------------
                long milliseconds = currentStatAggregate.aggregateTimer.ElapsedMilliseconds - milis;
                if (currentStatAggregate.maxUpdateMilliseconds < milliseconds)
                    currentStatAggregate.maxUpdateMilliseconds = milliseconds;
                currentStatAggregate.totalUpdateMilliseconds += milliseconds;

                //------------------------------------
                //  如果经过的时间大于AGGREGATION_MILLISECONDS（即250毫秒），则计算帧频fps
                //------------------------------------
                if (currentStatAggregate.aggregateTimer.ElapsedMilliseconds >= AGGREGATION_MILLISECONDS)
                {
                    lastFps = currentStatAggregate.frameCount * 1000f / (float)currentStatAggregate.aggregateTimer.ElapsedMilliseconds;

                    //将数据复制到lastStatAggregate中，然后重置
                    currentStatAggregate.CopyTo(ref lastStatAggregate);
                    currentStatAggregate.Reset();
                }
            }
        }

        /// <summary>
        /// 绘制scene
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Draw(GameTime gameTime)
        {
            //  获取当前流逝的时间，单位为毫秒
            long milis = currentStatAggregate.aggregateTimer.ElapsedMilliseconds;

            GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, engine.BackGroundColor, 1.0f, 0);

            if (activeScene != null)
            {
                activeScene.Camera.hasChanged = false;
                
                //绘制scene集合中的所有scene
                for (int i = 0; i < scenes.Count; i++)
                {
                    if (scenes[i].ScreenState == ScreenState.Hidden)
                        continue;
                    scenes[i].Draw(gameTime);
                }                

                //------------------------------------
                //  当前绘制花费的时间，单位毫秒
                //------------------------------------
                long milliseconds = currentStatAggregate.aggregateTimer.ElapsedMilliseconds - milis;
                if (currentStatAggregate.maxFrameRenderMilliseconds < milliseconds)
                    currentStatAggregate.maxFrameRenderMilliseconds = milliseconds;

                currentStatAggregate.totalRenderMilliseconds += milliseconds;

                //---------------------
                //  计算FPS
                //---------------------
                currentStatAggregate.frameCount++;
            }

#if DEBUG
                //在屏幕上显示统计信息
                DrawInformation();                
#endif
        }

        #endregion

        #region 其他方法，主要是添加场景和移除场景

        /// <summary>
        /// 根据scene的名称返回一个scene
        /// </summary>
        /// <param name="name">scene的名称</param>
        /// <returns></returns>
        public Scene GetSceneByName(string name)
        {

            if (!sceneIdForNames.ContainsKey(name))
                throw new KeyNotFoundException("给定的Scene名称不存在");
            return scenes[sceneIdForNames[name]];

        }

        /// <summary>
        /// 根据scene的ID返回一个scene
        /// </summary>
        /// <param name="sceneId">scene的ID</param>
        /// <returns></returns>
        public Scene GetSceneById(ushort sceneId)
        {

            if (sceneId > scenes.Count)
                throw new IndexOutOfRangeException("ID必须小于Scene对象的数量");

            return scenes[sceneId - 1];
        }

        /// <summary>
        /// 将一个scene添加到SceneManager中的scene集合
        /// </summary>
        /// <param name="scene">要添加的scene</param>
        public void AddScene(Scene scene)
        {
            if (sceneIdForNames.ContainsKey(scene.Name))
                throw new InvalidOperationException("给定的Scene名称已经存在了");

            //添加到字典
            scene.sceneId = sceneId++;
            sceneIdForNames.Add(scene.Name, scene.sceneId);

            scene.SceneManager = this;
            scene.IsExiting = false;

            // 如果图形设备已经建立，加载secne的内容
            if (isInitialized)
            {
                scene.Initialize();
            }
            scenes.Add(scene);

            //将最后加入的scene作为当前激活的scene
            activeScene = scenes[scenes.Count - 1];
        }

        /// <summary>
        /// 从集合中移除一个scene。通常需要使用Scene.ExitScreen方法替代直接调用这个方法
        /// 这样才能实现淡出效果
        /// </summary>
        public void RemoveScene(Scene scene)
        {
            // 如果图形设备已建立，则移除scene的内容
            if (isInitialized)
            {
                scene.UnloadContent();
            }

            sceneIdForNames.Remove(scene.Name);
            scenes.Remove(scene);
            scenesToUpdate.Remove(scene);

            //将集合中的最后一个scene作为当前激活的scene
            if (scenes.Count > 0)
                activeScene = scenes[scenes.Count - 1];
        }

        /// <summary>
        /// 返回包含所有scene的数组
        /// </summary>        
        public Scene[] GetScenes()
        {
            return scenes.ToArray();
        }

        /// <summary>
        /// 创建一个新Scene对象并把它添加到Scenes集合中        
        /// </summary>
        /// <param name="name"></param>
        /// <returns>The newly created Scene object</returns>
        public Scene CreateScene(string name)
        {

            if (sceneIdForNames.ContainsKey(name))
                throw new InvalidOperationException("The given name is already in use.");

            Scene scene = new Scene(engine, name, sceneId++);
            sceneIdForNames.Add(name, scene.sceneId);
            scenes.Add(scene);
            return scene;
        }

        /// <summary>
        /// 在屏幕上显示统计信息
        /// </summary>
        private void DrawInformation()
        {
            #if DEBUG
            
            string Info= string.Format("Camera:Position = {0} H = {1:000.0000} V = {2:000.0000}\n"+
            "Enabled={3:000#},Updated={4:000#},Visible={5:000#},rendered={6:000#}\n"+
            "triangles={7},uiNodes={8},aabb culled={9:000#},d.culled={10:000#}", 
            activeScene.Camera.Pose.Position, activeScene.Camera.AngleRotation.Y,activeScene.Camera.AngleRotation.X,
activeScene.lastStat.nodesEnabled, activeScene.lastStat.nodesUpdated,activeScene.lastStat.nodesVisible, activeScene.lastStat.nodesRendered,
activeScene.lastStat.trisRendered, activeScene.lastStat.uiNodesRendered, activeScene.lastStat.nodesBoundingBoxCulled, activeScene.lastStat.nodesDistanceCulled);            
            string fps = string.Format("FPS: {0:0.000}", Fps);

            RainGE.SpriteBatch.Begin();
            RainGE.SpriteBatch.DrawString(engine.DefaultFont, fps, new Vector2(engine.GraphicsDevice.Viewport.X + 5, engine.GraphicsDevice.Viewport.Y), Color.Red);
            RainGE.SpriteBatch.DrawString(engine.DefaultFont, Info, new Vector2(engine.GraphicsDevice.Viewport.X + 5, engine.GraphicsDevice.Viewport.Height - 75), Color.White);
            RainGE.SpriteBatch.End();
#endif
        }
        #endregion
    }
}
